//SA omp
//$Id: sa_omp.h 175 2009-09-12 17:34:23Z Oleg.Bulychov $
#pragma once
#ifndef SA_OMP_H
#define SA_OMP_H

#include "src/heo/include/easy_omp.h"
#include "sa_common.h"
//__________________________________________________________________________________
/**
The Multistart Model with Ring Cooperation
*/
template<class P, class S, class C>
class SA_OMP:
    public SA<P, S, C>
{
public:
    typedef P TProblem;
    typedef S TSolution;
    typedef C TConfig;
    typedef typename S::TDirection TDirection;

public:
    static void solve(TProblem const& problem, TSolution& sol, TConfig const& cfg)
    {
        int np = omp_get_max_threads();
        parallel_sa psa(problem, sol, cfg);
        omp::communicator::run(np, psa);
    }

private:

    struct parallel_sa
    {
        TProblem const& problem_;
        TSolution&      sol_;
        TConfig const&  cfg_;

        explicit parallel_sa(TProblem const& problem, TSolution& sol, TConfig const& cfg):
            problem_(problem), sol_(sol), cfg_(cfg)
        {}

        void operator()(omp::communicator& comm)
        {
            int np   = comm.size();
            int rank = comm.rank();
            int prev = (np + rank - 1) % np;
            int next = (rank + 1)      % np;

            uniform_random rnd(rank);

            TSolution sol;
            sol.init(problem_, cfg_);
            sol.generate_Trivial();

            TSolution tmp;
            tmp.init(problem_, cfg_);

            for (int N = 1; N <= cfg_.run_count_; ++N)
            {
                SA_OMP<TProblem, TSolution, TConfig> sa;
                sa.init(problem_, cfg_);
                sa.generate_Random_Solution(problem_, cfg_, rnd);
                sa.set_Initial_Temperature(problem_, tmp, cfg_, rnd, rank);
                sa.update_Solution(sol, cfg_, 0, rank);
                for (int step = 1; step <= cfg_.max_step_; ++step)
                {
                    if (!ring_cooperation<omp::recv, omp::isend, omp::isend>::cooperate(sa,
                            rank, prev, next, np, tmp, cfg_, step, comm))
                    {
                        sa.move(problem_, tmp, cfg_, rnd);
                    }
                    if (sa.is_Acceptable(tmp, rnd))
                    {
                        sa.accept(tmp);
                        sa.update_Solution(sol, cfg_, step, rank);
                    }
                    ////:if (sol.is_Solved(problem_))
                    ////{
                    ////    std::cout << "solved at step " << step << lf;
                    ////    break;
                    ////}
                    sa.update_Temperature(cfg_, step, rank);
                }
                sa.clear_OMP(cfg_, comm);
#pragma omp critical
                if (TDirection()(sol.get_Cost(), sol_.get_Cost()))
                {
                    std::cout << rank << ":*sol = " << sol.get_Cost() << lf;
                    sol_.assign(sol);
                }
                else
                {
                    std::cout << rank << ": sol = " << sol.get_Cost() << lf;
                }
            }
        }
    };

    void clear_OMP(TConfig const& cfg, omp::communicator& comm)
    {
        comm.barrier();
        if (!cfg.async_mode_)
            return;
        comm.clear_Data();
        comm.barrier();
    }
};
//__________________________________________________________________________________
//__________________________________________________________________________________

#endif
//EOF!
